// encoding utf-8
#ifndef __QyGLInputController__
#define __QyGLInputController__

#include <QGraphicsSceneMouseEvent>
#include <QyMath.h>

class QyWidgetEventFilter;

namespace QyGL {

class Scene;

/*! \brief Событие от мыши, содержащее пространственные координаты указателя. Координаты формируются в XY плоскости с заданным Z
	\ingroup QyOpenGL
*/
class MouseEvent : public QMouseEvent {
public:
	explicit MouseEvent( QGraphicsSceneMouseEvent * event, const QPoint & point, const QyMath::Vector & glCursor );

	qreal glX() const { return p_glCursor.p[0]; } //!< Координата X
	qreal glY() const { return p_glCursor.p[1]; } //!< Координата Y
	qreal glZ() const { return p_glCursor.p[2]; } //!< Координата Z
	QyMath::Vector glPos() const { return p_glCursor; } //!< Координаты в пространстве, упакованные в вектор

private:
	QyMath::Vector p_glCursor;

};


/*! \brief Контроллер ввода для виджета - OpenGL представления Scene
	\ingroup QyOpenGL

Контроллер сам по себе никаких конечных операций не производит.

Контроллер предназначен для построения ни его базе различных обработчиков для Scene.
Рассмотрим такой пример:
У нас есть некий графический редактор аля MS PaintBrush ( :-p ) и нам необходимо сделать для него
набор инструментов для рисования линий, эллипсов, и прямоугольников. Т.е. редактор почти готов, у
него есть панель инструментов, окно для рисования и т.д.<br>
Для каждого из этих действий нам потребуется как минимум обработка нажатия кнопки мыши, ее
перемещение и событие освобождения кнопки мыши (юзер кнопку отпустил).<br>
InputController позволяет обрабатывать эти события. Мы берем, и для каждого инструмента определяем
новый класс-наследник от InputController, в которых переопределяем виртуальные методы: mousePressd,
mouseMoved, mouseReleased:

\code
class DrawLine : public InputController {
...
	virtual void mousePressd( const MouseEvent & event );
	virtual void mouseMoved( const MouseEvent & event );
	virtual void mouseReleased( const MouseEvent & event );
...
};
class DrawEllipse : public InputController {
...
	virtual void mousePressd( const MouseEvent & event );
	virtual void mouseMoved( const MouseEvent & event );
	virtual void mouseReleased( const MouseEvent & event );
...
};
class DrawRect : public InputController {
...
	virtual void mousePressd( const MouseEvent & event );
	virtual void mouseMoved( const MouseEvent & event );
	virtual void mouseReleased( const MouseEvent & event );
...
};
\endcode

Для каждого класса в обработчиках пишем код рисующий: линии, эллипсы и прямоугольники.<br>

Теперь создаем по экземпляру каждого класса. Все контроллеры привязываются к одному объекту типа
Scene.

Получается что мы имеем 3 независимых друг от друга контроллера, включая (setEnabled(true)) и
выключая которые мы можем рисовать: линию, эллипс или прямоугольник, причяем рисовать можно например
линию и прямоугольник одновременно. Комбинации не важны. Важно то, что при такой системе, функцианал
редактора проще расширять, каждый рас когда нам потребовался новый инструмент, мы пишем новый
контроллер и привязываем его к отображения.

	\ingroup QyOpenGL
*/
class InputController {
public:
    friend class QyWidgetEventFilter;

	explicit InputController( Scene * target = 0 );
	virtual ~InputController();

	Scene * scene() const; //!< Отображение к которому привячзан контроллер
	bool isEnabled() const { return p_enabled; } //!< Состояние контроллера (вкл/выкл)
	bool isMousePressed() const { return p_mousePressed; } //!< Нажата ли кнопка мыши (любая)
	quint32 pressButtons() const { return p_pressButtons; } //!< Нажатая кнопка мыши
	QPointF pressPos() const { return p_pressPos; } //!< Координаты нажатия кнопки мыши, оконные координаты
	QPointF pressOffset() const { return p_pressOffset; } //!< Смещение мыши относительно точки нажатия (на сколько отодвинул юзер мышь от точки нажатия), оконные координаты
	QPointF moveOffset() const { return p_moveOffset; } //!< Последнее смещение (на сколько юзер сместил мышь относительно последней ее позиции), оконные координаты
	QPointF lastMovePos() const { return p_lastMovePos; } //!< Поледняя точка в которой находилась мышь, оконные координаты
	QyMath::Vector glPressPos() const { return p_glPressPos; } //!< Координаты нажатия кнопки мыши, координаты сцены (пространственные)
	QyMath::Vector glPressOffset() const { return p_glPressOffset; } //!< Смещение мыши относительно точки нажатия (на сколько отодвинул юзер мышь от точки нажатия), координаты сцены (пространственные)
	QyMath::Vector glMoveOffset() const { return p_glMoveOffset; } //!< Последнее смещение (на сколько юзер сместил мышь относительно последней ее позиции), координаты сцены (пространственные)
	QyMath::Vector glLastMovePos() const { return p_glLastMovePos; } //!< Поледняя точка в которой находилась мышь, координаты сцены (пространственные)

	void setEnabled( bool enabled ) { p_enabled = enabled; enabledChanged(p_enabled); } //!< Установить состояние контроллера (вкл/выкл)
	void setDisabled( bool disabled ) { p_enabled = !disabled; enabledChanged(p_enabled); } //!< Установить состояние контроллера (вкл/выкл)
	virtual void loadState( QDataStream & ) {} //!< Загрузить некоторое состояние контроллера из потока
	virtual void saveState( QDataStream & ) {} //!< Сохранить некоторое состояние контроллера в поток

	static bool match( const QGraphicsSceneMouseEvent * event, int buttons, int modifiers=-1 ); //!< Проверить "нажатасть" кнопки(ок) мыши в событии
	static bool match( const QKeyEvent * event, int key, int modifiers=-1 ); //!< Проверить "нажатасть" кнопки(ок) клавиатуры в событии

// protected:
	virtual void mousePressd( const MouseEvent & ) {} //!< Обработчик нажатия кнопки мыши
	virtual void mouseMoved( const MouseEvent & ) {} //!< Обработчик перемещения мыши
	virtual void mouseReleased( const MouseEvent & ) {} //!< Обработчик освобождения кнопки мыши
	virtual void keyPressd( QKeyEvent * ) {} //!< Обработчик нажатия кнопки на клавиатуре
	virtual void keyReleased( QKeyEvent * ) {} //!< Обработчик освобождения кнопки на клавиатуре
	virtual void enabledChanged( bool ) {} //!< Состояние контроллера изменилось

// private:
	QyWidgetEventFilter * p_widgetEventFilter;
	bool p_enabled, p_mousePressed;
	quint32 p_pressButtons;
	QPointF p_pressPos, p_pressOffset, p_moveOffset, p_lastMovePos;
	QyMath::Vector p_glPressPos, p_glPressOffset, p_glMoveOffset, p_glLastMovePos;

};


/*! \brief Контроллер управления камерой при помощи мыши, умеет поворячивать камеру по осям XZ, перемещать камеру по XYZ (Z - работает как масштабирование)
	\ingroup QyOpenGL

	По умолчанию управление следующее:
	- Зажатая средняя кнопка - перемещение;
	- Зажатая правая - обзор;
	- Зажатые правая и левая - наезд.

	\ingroup QyOpenGL
*/
class BasicCamViewController : public InputController {
public:
	//! Режимы контроллера
	enum Mode {
		None, //!< Нет
		Move, //!< Перемещение
		Rotate, //!< Поворот (обзор)
		Zoom //!< Наезд (Масштабирование)
	};
	explicit BasicCamViewController( Scene * target = 0 )
		: InputController(target), minZoom(0.0), maxZoom(100.0)
	{

		p_modeButtons[Move] = Qt::MidButton;
		p_modeButtons[Rotate] = Qt::RightButton;
		p_modeButtons[Zoom] = Qt::LeftButton|Qt::RightButton;

	}
	virtual ~BasicCamViewController() {}

	int button( int mode ) const { return p_modeButtons[mode]; } //!< Получить сочетание кнопок мыши для режима
	int mode( int buttons ) const; //!< Поличить режим по сочитанию

	void setButtons( int mode, int buttons ) { p_modeButtons[mode] = buttons; } //!< Установить сочетание для режима

	qreal minZoom,
		maxZoom; //!< Максимальное и минимальное  значение для наезда

protected:
	virtual void mousePressd( const MouseEvent & event );
	virtual void mouseMoved( const MouseEvent & event );

private:
	QMap<int,int> p_modeButtons;
	QyMath::Vector p_pressCamPos;

};

/*! \brief Контроллер управления камерой с клавиатуры.
	\ingroup QyOpenGL

	Кнопки управления покачто менять нельзя, значения стандартные для любого шутера: WASD

	\ingroup QyOpenGL
*/
class BaseKeyController : public InputController {
public:
	explicit BaseKeyController( Scene * target = 0 )
		: InputController(target) {}
	virtual ~BaseKeyController() {}

	QList<int> keys() const { return p_keys; }
	bool match( int key ) const { return p_keys.contains(key); }

protected:
	virtual void keyPressd( QKeyEvent * );
	virtual void keyReleased( QKeyEvent * );

private:
	QList<int> p_keys;

};

};

#endif/*__QyGLInputController__*/
